/*
 * Copyright (C) 2014 Sony Computer Entertainment Inc.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "URLRequestHttp.h"

#if USE(NTF)

#include "CredentialBackingStoreManx.h"
#include "CredentialTransformDataManx.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
#include "ResourceHandle.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceRequest.h"

// #define LISTENER_TRACE_ON

#if defined(LISTENER_TRACE_ON)
#define TRACE(...)   { printf("[Listener 0x%p] ", this); printf(__VA_ARGS__); }
#else
#define TRACE(...) ((void)0)
#endif

namespace WebCore {

// URLRequestHttpFactory ------------------------------------------------------

NTF::URLRequestMessage* URLRequestHttpFactory::createRequestMessage(ResourceHandle* resourceHandle)
{
    ResourceHandleInternal* d = resourceHandle->getInternal();

    // Prepare request message

    bool isMainResource = resourceHandle->firstRequest().isMainResource();

    KURL requestUrl = resourceHandle->firstRequest().url();
    String url = requestUrl.string();
    String httpMethod = resourceHandle->firstRequest().httpMethod();

    // Create RequestMessage

    NTF::URLRequestHttpMessage* message = NTF::URLRequestHttpMessage::create();

    message->setIsMainResource(isMainResource);
    message->setSchedulerId(0);
    // url is in ASCII so latin1() will only convert it to char* without character translation.
    message->setUrl(url.latin1().data());
    message->setHttpMethod(httpMethod.latin1().data());

    // Header Field
    if (resourceHandle->firstRequest().httpHeaderFields().size() > 0) {
        HTTPHeaderMap customHeaders = resourceHandle->firstRequest().httpHeaderFields();
        HTTPHeaderMap::const_iterator end = customHeaders.end();
        for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) {
            String key = it->key;
            String value = it->value;

            message->appendHeaderStrings(key.latin1().data(), value.latin1().data());
        }
    }

    // Body (Form) data
    RefPtr<FormData> formData = resourceHandle->firstRequest().httpBody();

#if ENABLE(BLOB)
    // Resolve the blob elements so the formData can correctly report it's size.
    if (formData) {
        formData = formData->resolveBlobReferences();
        resourceHandle->firstRequest().setHTTPBody(formData);
    }
#endif

    if (formData) {
        size_t numElements = formData->elements().size();

        for (size_t i = 0; i < numElements; i++) {
            const FormDataElement& element = formData->elements()[i];

            if (element.m_type == FormDataElement::data)
                message->formData()->appendData(element.m_data.data(), element.m_data.size());
            else {
                ASSERT(element.m_type == FormDataElement::encodedFile);
                message->formData()->appendFile(element.m_filename.utf8().data());
            }
        }
    }

    // Timeout interval
    long timeoutMilliSeconds = 0;
    double timeoutSeconds = resourceHandle->firstRequest().timeoutInterval();
    if (timeoutSeconds > 0)
        timeoutMilliSeconds = static_cast<long>(timeoutSeconds * 1000.0);

    message->setTransferTimeoutIntervalMilliseconds(timeoutMilliSeconds);

    // HTTP Authentication
    ProtectionSpaceServerType protectServerType;
    ProtectionSpaceAuthenticationScheme protectAuthScheme;
    WebCore::Credential savedHttpCredential = CredentialBackingStore::instance()->getLogin(requestUrl, protectServerType, protectAuthScheme);

    if (!savedHttpCredential.isEmpty()) {
        NTF::AuthenticationScheme authScheme = NTF::AuthenticationSchemeHTTPDigest;

        if (protectAuthScheme == ProtectionSpaceAuthenticationSchemeHTTPBasic)
            authScheme = NTF::AuthenticationSchemeHTTPBasic;

        String user = savedHttpCredential.user();
        String password = savedHttpCredential.password();

        if (message->httpAuthCredential())
            message->httpAuthCredential()->setCredential(authScheme, user.latin1().data(), password.latin1().data());
    }

    // TODO :
    //   cookieJarFileName

    // Set response ------------------

    d->m_response.setIsMainResource(isMainResource);

    return static_cast<NTF::URLRequestMessage*>(message);
}



// URLRequestHttpListener -----------------------------------------------------

URLRequestHttpListener::URLRequestHttpListener(ResourceHandle* resourceHandle)
    : URLRequestListenerNtf(resourceHandle)
{

}

URLRequestHttpListener::~URLRequestHttpListener()
{

}

bool URLRequestHttpListener::didReceiveResponseMessage(NTF::URLResponseMessage* responseMessage)
{
    ASSERT(responseMessage);

    bool canReleaseHandle = false;

    switch (responseMessage->getMessageType()) {
    case NTF::URLResponseMessage::HttpResponseHeaderFields :
        canReleaseHandle = didReceiveResponseHeaderFieldsMessage(static_cast<NTF::URLResponseHttpHeaderFieldsMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpResponseHeader :
        canReleaseHandle = didReceiveResponseHeaderMessage(static_cast<NTF::URLResponseHttpHeaderMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpResponseBody :
        canReleaseHandle = didReceiveResponseBodyMessage(static_cast<NTF::URLResponseHttpBodyMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpResponseRedirect :
        canReleaseHandle = didReceiveResponseRedirectMessage(static_cast<NTF::URLResponseHttpRedirectMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpResponseFinish :
        canReleaseHandle = didReceiveResponseFinishMessage(static_cast<NTF::URLResponseHttpFinishMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpResponseFail :
        canReleaseHandle = didReceiveResponseFailMessage(static_cast<NTF::URLResponseHttpFailMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpAuthenticationChallenge :
        canReleaseHandle = didReceiveAuthenticationChallengeMessage(static_cast<NTF::URLResponseHttpAuthenticationChallengeMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpAuthenticationSuccess :
        canReleaseHandle = didReceiveAuthenticationSuccessMessage(static_cast<NTF::URLResponseHttpAuthenticationSuccessMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpDetermineMimeType :
        canReleaseHandle = didReceiveDetermineMimeTypeMessage(static_cast<NTF::URLResponseHttpDetermineMimeTypeMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpSslCertOriginFrame :
        canReleaseHandle = didReceiveSslCertOriginFrameMessage(static_cast<NTF::URLResponseHttpSslCertOriginFrameMessage*>(responseMessage));
        break;

    case NTF::URLResponseMessage::HttpSslCertError :
        canReleaseHandle = didReceiveSslCertErrorMessage(static_cast<NTF::URLResponseHttpSslCertErrorMessage*>(responseMessage));
        break;

    default:
        ASSERT(false);
    }

    return canReleaseHandle;
}


// Response Header Field ----------------------------------

bool URLRequestHttpListener::didReceiveResponseHeaderFieldsMessage(NTF::URLResponseHttpHeaderFieldsMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);

    if (d->m_cancelled)
        return false;

    const char* url = message->url();
    long responseCode = message->responseCode();
    long long contentLength = message->contentLength();
    int headerFieldSize = message->headerFieldSize();

    TRACE("url = %s\n", url);
    TRACE("responseCode = %ld\n", responseCode);
    TRACE("contentLength = %lld\n", contentLength);
    TRACE("headerFieldSize = %d\n", headerFieldSize);

    for (int headerFieldPos = 0; headerFieldPos < headerFieldSize; headerFieldPos++) {
        const void* data = 0;
        size_t dataSize = 0;

        message->headerField(headerFieldPos, &data, &dataSize);
        if (!data)
            continue;

        String header = String::fromUTF8WithLatin1Fallback(static_cast<const char*>(data), dataSize);
        TRACE("headerField = %s\n", header.stripWhiteSpace().latin1().data());

        int splitPos = header.find(':');
        if (splitPos != -1) {
            String key = header.left(splitPos).stripWhiteSpace();
            String value = header.substring(splitPos + 1).stripWhiteSpace();

            // TODO : See curl port.
            // if (isAppendableHeader(key))
            //     d->m_response.addHTTPHeaderField(key, value);

            d->m_response.setHTTPHeaderField(key, value);
        } else if (header.startsWith("HTTP", false)) {
            // This is the first line of the response.
            // Extract the http status text from this.
            //
            // If the FOLLOWLOCATION option is enabled for the curl handle then
            // curl will follow the redirections internally. Thus this header callback
            // will be called more than one time with the line starting "HTTP" for one job.

            String responseCodeString = String::number(responseCode);
            int responseCodePos = header.find(responseCodeString);

            if (responseCodePos != -1) {
                // The status text is after the status code.
                String status = header.substring(responseCodePos + responseCodeString.length());
                d->m_response.setHTTPStatusText(status.stripWhiteSpace());
            }
        }
    }

    d->m_response.setURL(KURL(KURL(), url));
    d->m_response.setHTTPStatusCode(responseCode);
    d->m_response.setExpectedContentLength(contentLength);

    d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type")));
    d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type")));
    d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition")));

    return false;
}


// Response Header ----------------------------------------

bool URLRequestHttpListener::didReceiveResponseHeaderMessage(NTF::URLResponseHttpHeaderMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    ResourceHandleClient* client = d->client();

    if (d->m_cancelled)
        return false;

    if (client)
        client->didReceiveResponse(resourceHandle, d->m_response);

    d->m_response.setResponseFired(true);

    return false;
}


// Response Body ------------------------------------------

bool URLRequestHttpListener::didReceiveResponseBodyMessage(NTF::URLResponseHttpBodyMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    ResourceHandleClient* client = d->client();

    if (d->m_cancelled)
        return false;

    const char* url = message->url();
    const char* data = static_cast<const char*>(message->data());
    int dataSize = static_cast<int>(message->dataSize());

    if (!d->m_response.responseFired()) {
        // since the code in headerCallback will not have run for local files
        // the code to set the URL and fire didReceiveResponse is never run,
        // which means the ResourceLoader's response does not contain the URL.
        // Run the code here for local files to resolve the issue.
        // TODO: See if there is a better approach for handling this.
        d->m_response.setURL(KURL(KURL(), url));
        if (client)
            client->didReceiveResponse(resourceHandle, d->m_response);
        d->m_response.setResponseFired(true);

        if (d->m_cancelled)
            return false;
    }

    if (client)
        client->didReceiveData(resourceHandle, data, dataSize, 0);

    return false;
}


// Response Redirect --------------------------------------

bool URLRequestHttpListener::didReceiveResponseRedirectMessage(NTF::URLResponseHttpRedirectMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    ResourceHandleClient* client = d->client();

    if (d->m_cancelled)
        return false;

    const char* url = message->url();
    String httpMethod(message->httpMethod());

    if (url) {
        KURL redirectUrl = KURL(KURL(), url);
        ResourceRequest redirectedRequest = resourceHandle->firstRequest();
        redirectedRequest.setRedirected(true);
        redirectedRequest.setURL(redirectUrl);

        if ((httpMethod == "GET") && (httpMethod != redirectedRequest.httpMethod())) {
            redirectedRequest.setHTTPMethod("GET");
            redirectedRequest.setHTTPBody(0);
            redirectedRequest.clearHTTPContentType();
            redirectedRequest.clearHTTPReferrer();
        }

        if (client)
            client->willSendRequest(resourceHandle, redirectedRequest, d->m_response);

        redirectedRequest.setRedirected(false);
        d->m_firstRequest.setURL(redirectUrl);
    }

    return false;
}


// Response Finish ----------------------------------------

bool URLRequestHttpListener::didReceiveResponseFinishMessage(NTF::URLResponseHttpFinishMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    ResourceHandleClient* client = d->client();

    if (!d->m_cancelled) {
        const char* url = message->url();

        if (!d->m_response.responseFired()) {
            // since the code in headerCallback will not have run for local files
            // the code to set the URL and fire didReceiveResponse is never run,
            // which means the ResourceLoader's response does not contain the URL.
            // Run the code here for local files to resolve the issue.
            // TODO: See if there is a better approach for handling this.
            d->m_response.setURL(KURL(KURL(), url));
            if (client)
                client->didReceiveResponse(resourceHandle, d->m_response);
            d->m_response.setResponseFired(true);
        }
    }

    // There is a possibility that changes the cancelled flag in the didReceiveResponse().
    // So, check the cancelled flag again.
    if (!d->m_cancelled) {
        if (client)
            client->didFinishLoading(resourceHandle, 0);
    }

    return true;
}


// Response Fail ------------------------------------------

bool URLRequestHttpListener::didReceiveResponseFailMessage(NTF::URLResponseHttpFailMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    ResourceHandleClient* client = d->client();

    // Memo : In this case, we does not check the cancelled flag.
    //        We will notify the cancellation using the ResourceError.

    int errorCode = static_cast<int>(message->resultCode());
    String failingURL(message->url());
    String localizedDescription(message->resultString());

    ResourceError resourceError(String(), errorCode, failingURL, localizedDescription);

    if (message->isCancellation())
        resourceError.setIsCancellation(true);

    if (message->isTimeout())
        resourceError.setIsTimeout(true);

    if (client)
        client->didFail(resourceHandle, resourceError);

    return true;
}


// Authentication Challenge -------------------------------

bool URLRequestHttpListener::didReceiveAuthenticationChallengeMessage(NTF::URLResponseHttpAuthenticationChallengeMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    NetworkingContext* context = resourceHandle->context();
    ASSERT(context);

    KURL kurl(KURL(), message->url());
    NTF::AuthenticationChallengeHandler* challengeHandler = message->authenticationChallengeHandler();
    NTF::AuthenticationTarget authTarget = message->authenticationTarget();
    NTF::AuthenticationScheme authScheme = message->authenticationScheme();
    const char* realm = message->realm();

    if (d->m_cancelled) {
        if (challengeHandler)
            challengeHandler->didReceiveAuthenticationCancellation();
        return false;
    }

    // Preapare a ProtectionSpace.
    ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP;
    if (authTarget == NTF::AuthenticationTargetHttp) {
        if (kurl.protocolIs("https"))
            serverType = ProtectionSpaceServerHTTPS;
    } else
        serverType = ProtectionSpaceProxyHTTP;

    ProtectionSpaceAuthenticationScheme protectAuthScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
    if (authScheme == NTF::AuthenticationSchemeHTTPBasic)
        protectAuthScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;

    ProtectionSpace space = ProtectionSpace(kurl.host(), kurl.port(), serverType, realm, protectAuthScheme);

    // Get credential from cache or user.
    Credential savedCredential = CredentialBackingStore::instance()->getLogin(space);
    if (!savedCredential.isEmpty()) {
        // Get a credential from cache.
        if (challengeHandler)
            challengeHandler->didReceiveAuthenticationCredential(authTarget, authScheme, savedCredential.user().latin1().data(), savedCredential.password().latin1().data());
    } else {
        // Get a credential from user.
        AuthenticationChallenge challenge;
        Credential cred;
        ResourceResponse resp;
        ResourceError err;

        challenge = WebCore::AuthenticationChallenge(space, cred, 0, resp, err);

        // Back up the authentication information to save the realm in ProtectionSpace.
        if (authTarget == NTF::AuthenticationTargetHttp)
            d->m_httpWebChallenge = challenge;
        else
            d->m_proxyWebChallenge = challenge;

        // Ask the user to enter authentication credential.
        context->onAuthenticationRequired(challenge);

        // Confirm authentication result by user.
        if (challengeHandler) {
            if (challenge.m_ok)
                challengeHandler->didReceiveAuthenticationCredential(authTarget, authScheme, challenge.m_user.latin1().data(), challenge.m_pass.latin1().data());
            else
                challengeHandler->didReceiveAuthenticationCancellation();
        }
    }

    return false;
}


// Authentication Success ---------------------------------

bool URLRequestHttpListener::didReceiveAuthenticationSuccessMessage(NTF::URLResponseHttpAuthenticationSuccessMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);

    if (d->m_cancelled)
        return false;

    bool isProxy = (message->authenticationTarget() == NTF::AuthenticationTargetHttp) ? false : true;

    KURL kurl(KURL(), message->url());
    const ProtectionSpace& space = (!isProxy) ? d->m_httpWebChallenge.protectionSpace() : d->m_proxyWebChallenge.protectionSpace();

    Credential inputCredential(message->authenticationCredential().username(), message->authenticationCredential().password(), CredentialPersistencePermanent);
    CredentialTransformData data = CredentialTransformData(kurl, space, inputCredential);

    Credential newSavedCredential = CredentialBackingStore::instance()->getLogin(data.url());
    if (newSavedCredential != data.credential()) {
        if (newSavedCredential.isEmpty())
            CredentialBackingStore::instance()->addLogin(kurl, data.protectionSpace(), data.credential());
        else
            CredentialBackingStore::instance()->updateLogin(kurl, data.protectionSpace(), data.credential());
    }

    return false;
}


// Determine MimeType -------------------------------------

bool URLRequestHttpListener::didReceiveDetermineMimeTypeMessage(NTF::URLResponseHttpDetermineMimeTypeMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);

    if (d->m_cancelled)
        return false;

    const char* url = message->url();

    d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url));

    return false;
}


// Ssl Cert OriginFrame -----------------------------------

bool URLRequestHttpListener::didReceiveSslCertOriginFrameMessage(NTF::URLResponseHttpSslCertOriginFrameMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);

    if (d->m_cancelled)
        return false;

    void* cert = message->cert();

    d->m_response.setCert(cert);

    return false;
}


// Ssl Cert Error -----------------------------------------

bool URLRequestHttpListener::didReceiveSslCertErrorMessage(NTF::URLResponseHttpSslCertErrorMessage* message)
{
    TRACE("[IN] %s(%d)\n", __FUNCTION__, __LINE__);

    ResourceHandle* resourceHandle = getResourceHandle();
    ASSERT(resourceHandle);
    ResourceHandleInternal* d = resourceHandle->getInternal();
    ASSERT(d);
    NetworkingContext* context = resourceHandle->context();
    ASSERT(context);

    KURL kurl(KURL(), message->url());
    NTF::SslCertErrorHandler* sslCertErrorHandler = message->sslCertErrorHandler();
    bool isMainResource = message->isMainResource();

    if (d->m_cancelled) {
        if (sslCertErrorHandler)
            sslCertErrorHandler->cancelProcess();
        return false;
    }

    bool isProceed = false;

    if (isMainResource) {
        ResourceError error(message->sslErrorCode(), (const LChar*)(message->url()));

#if OS(PSP2)
        error.setCert(message->chainForBadCertNotify());
#elif OS(ORBIS)
        Vector<CString> pems;
        for (int i = 0; i < message->pemsLength(); i++) {
            const char* pem = message->pems(i);
            if (pem)
                pems.insert(i, pem);
        }

        error.setCerts(pems);
#endif

        // Ask if the user accepts to proceed this process of the bad cert
        context->onCertificateVerificationRequest(error);

        isProceed = error.confirmCert();
    }

    if (isProceed)
        sslCertErrorHandler->proceedProcess();
    else
        sslCertErrorHandler->cancelProcess();

    return false;
}

} // namespace WebCore

#endif // #if USE(NTF)
